package me.flyray.common.exception;

import com.alibaba.fastjson.JSONException;
import com.fasterxml.jackson.core.JsonParseException;
import me.flyray.common.exception.auth.ClientTokenException;
import me.flyray.common.exception.auth.ImageCodeInvalidException;
import me.flyray.common.exception.auth.UserInvalidException;
import me.flyray.common.exception.auth.UserTokenException;
import me.flyray.common.msg.BaseApiResponse;
import me.flyray.common.msg.ResponseCode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/** 
* @author: bolei
* @date：2018年8月24日 下午12:37:59 
* @description：处理全局异常
*/

@RestControllerAdvice
public class GlobalExceptionHandler {

	private static final Logger LOGGER = LoggerFactory.getLogger(GlobalExceptionHandler.class);

    private static final String MISSING_PARAM_ERROR_CODE = "NotNull";

    /**
     * 应用到所有@RequestMapping注解方法，在其执行之前初始化数据绑定器
     * @param binder
     */
    @InitBinder
    public void initBinder(WebDataBinder binder) {

    }

    /**
     * 处理所有不可知的异常
     * @param e
     * @return
     */
    @ExceptionHandler(Exception.class)
    @ResponseBody
    BaseApiResponse handleException(Exception e, HttpServletRequest request){
        LOGGER.error(e.getMessage(), e);
        String requestURI = request.getRequestURI();
        LOGGER.error("occurs error when execute url ={} ,message {}",requestURI,e.getMessage());
        return BaseApiResponse.newFailure(ResponseCode.SYSTEM_ERROR.getCode(),e.getMessage());
    }

    /**
     * 处理API异常
     * @param e
     * @return
     */
    @ExceptionHandler(ApiException.class)
    @ResponseBody
    BaseApiResponse handleBusinessException(ApiException e, HttpServletRequest request){
        LOGGER.error(e.getMessage(), e);
        String requestURI = request.getRequestURI();
        LOGGER.error("occurs error when execute url ={} ,message {}",requestURI,e.getMessage());
        return BaseApiResponse.newFailure(e.getCode(),e.getMessage());
    }

    /**
     * 处理所有业务异常
     * @param e
     * @return
     */
    @ExceptionHandler(BusinessException.class)
    @ResponseBody
    BaseApiResponse handleBusinessException(BusinessException e, HttpServletRequest request){
        LOGGER.error(e.getMessage(), e);
        String requestURI = request.getRequestURI();
        LOGGER.error("occurs error when execute url ={} ,message {}",requestURI,e.getMessage());
        return BaseApiResponse.newFailure(e.getCode(),e.getMessage());
    }

    /**
     * 处理所有接口数据验证异常
     * @param e
     * @return
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    @ResponseBody
    BaseApiResponse handleMethodArgumentNotValidException(MethodArgumentNotValidException e,HttpServletRequest request){
        // 打印错误日志
        BindingResult bindingResult = e.getBindingResult();
        StringBuilder errLog = new StringBuilder();
        boolean isMissingParam = false;
        for (int i = 0; i < bindingResult.getFieldErrors().size(); i++) {
            if (i > 0) {
                errLog.append(",");
            }
            FieldError fieldError = bindingResult.getFieldErrors().get(i);
            errLog.append(fieldError.getField());
            errLog.append(":");
            errLog.append(fieldError.getDefaultMessage());
            for (String code : fieldError.getCodes()) {
                if (MISSING_PARAM_ERROR_CODE.equals(code)) {
                    isMissingParam = true;
                }
            }
        }
        LOGGER.warn(errLog.toString());
        BaseApiResponse<?> result =  isMissingParam ?  BaseApiResponse.newFailure(ResponseCode.REQUIRED_PARAMS_MISSING) :
                BaseApiResponse.newFailure(ResponseCode.INVALID_FIELDS);
        String retMsg = result.getMessage();
        retMsg = retMsg + ", " + errLog.toString();
        result.setMessage(retMsg);
        return result;
    }

    @ExceptionHandler(ClientTokenException.class)
    public BaseApiResponse clientTokenExceptionHandler(HttpServletResponse response, ClientTokenException ex) {
        response.setStatus(403);
        LOGGER.error(ex.getMessage(),ex);
        return BaseApiResponse.newFailure(String.valueOf(ex.getStatus()), ex.getMessage());
    }

    @ExceptionHandler(UserTokenException.class)
    public BaseApiResponse userTokenExceptionHandler(HttpServletResponse response, UserTokenException ex) {
        response.setStatus(406);
        LOGGER.error(ex.getMessage(),ex);
        return BaseApiResponse.newFailure(String.valueOf(ex.getStatus()), ex.getMessage());
    }

    @ExceptionHandler(UserInvalidException.class)
    public BaseApiResponse userInvalidExceptionHandler(HttpServletResponse response, UserInvalidException ex) {
        response.setStatus(401);
        LOGGER.error(ex.getMessage(),ex);
        return BaseApiResponse.newFailure(String.valueOf(ex.getStatus()), ex.getMessage());
    }

    @ExceptionHandler(ImageCodeInvalidException.class)
    public BaseApiResponse imageCodeInvalidExceptionHandler(HttpServletResponse response, ImageCodeInvalidException ex) {
        response.setStatus(400);
        LOGGER.error(ex.getMessage(),ex);
        return BaseApiResponse.newFailure(String.valueOf(ex.getStatus()), ex.getMessage());
    }
    @ExceptionHandler(BaseException.class)
    public BaseApiResponse baseExceptionHandler(HttpServletResponse response, BaseException ex) {
        LOGGER.error(ex.getMessage(),ex);
        response.setStatus(500);
        return BaseApiResponse.newFailure(String.valueOf(ex.getStatus()), ex.getMessage());
    }

    /**
     * 接收http参数解析报错，判断是否为Json解析异常
     */
    @ExceptionHandler(HttpMessageNotReadableException.class)
    @ResponseBody
    public BaseApiResponse<?> maformedHttpRequestHandler(HttpMessageNotReadableException ex) {
        // 覆盖Jackson与Fastjson的两个异常，如其他序列化框架，请自行添加
        if (ex.getCause() instanceof JSONException || ex.getCause() instanceof JsonParseException) {
            LOGGER.warn("maformed request json, stack trace: ", ex.getCause());
            return BaseApiResponse.newFailure(ResponseCode.INVALID_FIELDS);
        }
        LOGGER.warn("detected request parse exception, stack trace: ", ex);
        return BaseApiResponse.newFailure(ResponseCode.INVALID_FIELDS);
    }

}
